﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.Serialization;
using System.Threading;

namespace Assignment2 
{
    /// <summary>
    /// Serializable Order. Stores the information for a chicken order. Serializable for transport via the MultiCellBuffer
    /// </summary>
    [Serializable()] 
    public class Order : ISerializable
    {
        private int senderId; 
        private Int64 cardNumber; // changed this to Int64 to reflect 16-digit number - DTT - 9/23
        private int numberOfChickens;
        private DateTime orderPlacedTimestamp;
        private DateTime orderCompletedTimestamp;
        private double responseTime; // time difference between orderPoacedTimestamp and orderCompletedTimestamp in miliseconds

        private static int numberOfOrders = 0;
        private static double totalResponseTime = 0;
        private static Semaphore pool = new Semaphore(1, 1);

        private Int64 _completedPrice;
        private Int64 _subTotal;


        /// <summary>
        /// Order constructor. Initializes Order with default values.
        /// </summary>
        public Order()
        {
            this.senderId = -1;
            this.cardNumber = -1;
            this.numberOfChickens = -1;
            this.responseTime = -1;
        }

        /// <summary>
        /// Order constructor. Initializes Order with Retailer ID.
        /// </summary>
        /// <param name="senderId">Retailer ID as int.</param>
        public Order(int senderId)
        {
            this.senderId = senderId;
            this.cardNumber = -1;
            this.numberOfChickens = -1;
            this.responseTime = -1;
        }

        public void setSenderId(int senderId)
        {
            this.senderId = senderId;
        }

        public int getSenderId()
        {
            return this.senderId;
        }

        public void setCardNumber(Int64 cardNumber)
        {
            this.cardNumber = cardNumber;
        }

        public Int64 getCardNo()
        {
            return this.cardNumber;
        }

        public void setNumberOfChickens(int numberOfChickens)
        {
            this.numberOfChickens = numberOfChickens;
        }

        public int getNumberOfChickens()
        {
            return this.numberOfChickens;
        }

        /// <summary>
        /// Sets the orderPlacedTimestamp
        /// </summary>
        public void placeOrder()
        {
            this.orderPlacedTimestamp = DateTime.Now;
        }

        public DateTime getOrderPlacedTimestamp()
        {
            return this.orderPlacedTimestamp;
        }

        public void setOrderCompletedTimestamp(DateTime dateTime)
        {
            this.orderCompletedTimestamp = DateTime.Now;
        }

        /// <summary>
        /// Sets the orderCompletedTimestamp and calculates the total response time.
        /// </summary>
        public void completeOrder()
        {
            this.setOrderCompletedTimestamp(DateTime.Now);
            this.calculateResponseTime();
        }

        public DateTime getOrderCompletedTimestamp()
        {
            return this.orderCompletedTimestamp;
        }

        public void setOrderPlacedTimestamp(DateTime dateTime)
        {
            this.orderPlacedTimestamp = dateTime;
        }

        // get/set the completed order price
        public Int64 PriceTotal
        {
            get { return _completedPrice; }
            set { _completedPrice = value; }
        }

        // get/set for the sub total
        public Int64 SubTotal
        {
            get { return _subTotal; }
            set { _subTotal = value; }
        }

        /// <summary>
        /// Calculates the time difference between orderPlacedTimestamp and orderCompletedTimestamp
        /// </summary>
        private void calculateResponseTime()
        {
            if (this.orderCompletedTimestamp >= this.orderPlacedTimestamp)
            {
                string responseTimeString = "0";
                TimeSpan span = this.orderCompletedTimestamp - this.orderPlacedTimestamp;
                int seconds = span.Seconds;
                int millisecs = span.Milliseconds;
                if(millisecs < 10)
                    responseTimeString = seconds.ToString() + ".0" + millisecs.ToString();
                else
                    responseTimeString = seconds.ToString() + "." + millisecs.ToString();

                //this.responseTime = span.Milliseconds;

                this.responseTime = Double.Parse(responseTimeString);
            }
            else
            {
                this.responseTime = 0;
            }
            Logger.log_message("Response Time: " + this.responseTime);
            Order.addToAverageResponseTime(this.responseTime);
        }

        public double getResponseTime()
        {
            return this.responseTime;
        }

        public static void addToAverageResponseTime(double responseTime)
        {
            pool.WaitOne();
            totalResponseTime += responseTime;
            numberOfOrders++;
            pool.Release();
        }

        public static double getAverageResponseTime()
        {
            return totalResponseTime / numberOfOrders;
        }

        /// <summary>
        /// Returns a string representation of the Order
        /// </summary>
        /// <returns>Order as a String</returns>
        public override string ToString()
        {
            StringBuilder sb = new StringBuilder();
            sb.Append("Sender ID: " + this.senderId + "\r\n");
            sb.Append("Card Number: " + this.cardNumber + "\r\n");
            sb.Append("Number of Chickens: " + this.numberOfChickens + "\r\n");
            sb.Append("Order Date: " + this.orderPlacedTimestamp.ToString() + "\r\n");
            sb.Append("Completed Date: " + this.orderCompletedTimestamp.ToString() + "\r\n");
            sb.Append("Response Time: " + this.responseTime.ToString());
            return sb.ToString();
        }

        /// <summary>
        /// Deserialization constructor
        /// </summary>
        /// <param name="info"></param>
        /// <param name="ctxt"></param>
        public Order(SerializationInfo info, StreamingContext ctxt)
        {
            senderId = (int)info.GetValue("senderId", typeof(int));
            cardNumber = (Int64)info.GetValue("cardNumber", typeof(Int64));
            numberOfChickens = (int)info.GetValue("numberOfChickens", typeof(int));
            orderPlacedTimestamp = (DateTime)info.GetValue("orderPlacedTimestamp", typeof(DateTime));
            orderCompletedTimestamp = (DateTime)info.GetValue("orderCompletedTimestamp", typeof(DateTime));
            responseTime = (int)info.GetValue("responseTime", typeof(int));
        }
        

        /// <summary>
        /// Serialization function
        /// </summary>
        /// <param name="info"></param>
        /// <param name="ctxt"></param>
        public void GetObjectData(SerializationInfo info, StreamingContext ctxt)
        {
            info.AddValue("senderId", senderId);
            info.AddValue("cardNumber", cardNumber);
            info.AddValue("numberOfChickens", numberOfChickens);
            info.AddValue("orderPlacedTimestamp", orderPlacedTimestamp);
            info.AddValue("orderCompletedTimestamp", orderCompletedTimestamp);
            info.AddValue("responseTime", responseTime);
        }
    }
}
